﻿// Decompiled with JetBrains decompiler
// Type: PetaPoco.Database
// Assembly: 开发辅助工具, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null
// MVID: F78E5980-5E94-4E46-ABDF-DC8C6CBF52AA
// Assembly location: K:\Tools\工具\开发辅助工具庆六一版\开发辅助工具2015-06-01\开发辅助工具.exe

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace PetaPoco
{
  public class Database : IDisposable
  {
    private static Regex rxParams = new Regex("(?<!@)@\\w+", RegexOptions.Compiled);
    private static Regex rxParamsPrefix = new Regex("(?<!@)@\\w+", RegexOptions.Compiled);
    private static Regex rxColumns = new Regex("\\A\\s*SELECT\\s+((?:\\((?>\\((?<depth>)|\\)(?<-depth>)|.?)*(?(depth)(?!))\\)|.)*?)(?<!,\\s+)\\bFROM\\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.Singleline);
    private static Regex rxOrderBy = new Regex("\\bORDER\\s+BY\\s+(?:\\((?>\\((?<depth>)|\\)(?<-depth>)|.?)*(?(depth)(?!))\\)|[\\w\\(\\)\\.])+(?:\\s+(?:ASC|DESC))?(?:\\s*,\\s*(?:\\((?>\\((?<depth>)|\\)(?<-depth>)|.?)*(?(depth)(?!))\\)|[\\w\\(\\)\\.])+(?:\\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.Singleline);
    private static Regex rxDistinct = new Regex("\\ADISTINCT\\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.Singleline);
    private static Dictionary<string, object> MultiPocoFactories = new Dictionary<string, object>();
    private static Dictionary<string, object> AutoMappers = new Dictionary<string, object>();
    private static ReaderWriterLockSlim RWLock = new ReaderWriterLockSlim();
    private Database.DBType _dbType = Database.DBType.SqlServer;
    private Regex rxSelect = new Regex("\\A\\s*(SELECT|EXECUTE|CALL)\\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.Singleline);
    private Regex rxFrom = new Regex("\\A\\s*FROM\\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.Singleline);
    private string _paramPrefix = "@";
    private string _connectionString;
    private string _providerName;
    private DbProviderFactory _factory;
    private IDbConnection _sharedConnection;
    private IDbTransaction _transaction;
    private int _sharedConnectionDepth;
    private int _transactionDepth;
    private bool _transactionCancelled;
    private string _lastSql;
    private object[] _lastArgs;

    public bool KeepConnectionAlive { get; set; }

    public IDbConnection Connection
    {
      get
      {
        return this._sharedConnection;
      }
    }

    public bool EnableAutoSelect { get; set; }

    public bool EnableNamedParams { get; set; }

    public bool ForceDateTimesToUtc { get; set; }

    public int CommandTimeout { get; set; }

    public int OneTimeCommandTimeout { get; set; }

    public string LastSQL
    {
      get
      {
        return this._lastSql;
      }
    }

    public object[] LastArgs
    {
      get
      {
        return this._lastArgs;
      }
    }

    public string LastCommand
    {
      get
      {
        return this.FormatCommand(this._lastSql, this._lastArgs);
      }
    }

    public static IMapper Mapper { get; set; }

    public Database(IDbConnection connection)
    {
      this._sharedConnection = connection;
      this._connectionString = connection.ConnectionString;
      this._sharedConnectionDepth = 2;
      this.CommonConstruct();
    }

    public Database(string connectionString, string providerName)
    {
      this._connectionString = connectionString;
      this._providerName = providerName;
      this.CommonConstruct();
    }

    public Database(string connectionString, DbProviderFactory provider)
    {
      this._connectionString = connectionString;
      this._factory = provider;
      this.CommonConstruct();
    }

    public Database(string connectionStringName)
    {
      if (connectionStringName == "")
        connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
      string str = "System.Data.SqlClient";
      if (ConfigurationManager.ConnectionStrings[connectionStringName] == null)
        throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
      if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
        str = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
      this._connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
      this._providerName = str;
      this.CommonConstruct();
    }

    private void CommonConstruct()
    {
      this._transactionDepth = 0;
      this.EnableAutoSelect = true;
      this.EnableNamedParams = true;
      this.ForceDateTimesToUtc = true;
      if (this._providerName != null)
        this._factory = DbProviderFactories.GetFactory(this._providerName);
      string name = (this._factory == null ? (MemberInfo) this._sharedConnection.GetType() : (MemberInfo) this._factory.GetType()).Name;
      if (name.StartsWith("MySql"))
        this._dbType = Database.DBType.MySql;
      else if (name.StartsWith("SqlCe"))
        this._dbType = Database.DBType.SqlServerCE;
      else if (name.StartsWith("Npgsql"))
        this._dbType = Database.DBType.PostgreSQL;
      else if (name.StartsWith("Oracle"))
        this._dbType = Database.DBType.Oracle;
      else if (name.StartsWith("SQLite"))
        this._dbType = Database.DBType.SQLite;
      else if (name.StartsWith("System.Data.SqlClient."))
        this._dbType = Database.DBType.SqlServer;
      else if (this._providerName.IndexOf("MySql", StringComparison.InvariantCultureIgnoreCase) >= 0)
        this._dbType = Database.DBType.MySql;
      else if (this._providerName.IndexOf("SqlServerCe", StringComparison.InvariantCultureIgnoreCase) >= 0)
        this._dbType = Database.DBType.SqlServerCE;
      else if (this._providerName.IndexOf("Npgsql", StringComparison.InvariantCultureIgnoreCase) >= 0)
        this._dbType = Database.DBType.PostgreSQL;
      else if (this._providerName.IndexOf("Oracle", StringComparison.InvariantCultureIgnoreCase) >= 0)
        this._dbType = Database.DBType.Oracle;
      else if (this._providerName.IndexOf("SQLite", StringComparison.InvariantCultureIgnoreCase) >= 0)
        this._dbType = Database.DBType.SQLite;
      if (this._dbType == Database.DBType.MySql && this._connectionString != null && this._connectionString.IndexOf("Allow User Variables=true") >= 0)
        this._paramPrefix = "?";
      if (this._dbType != Database.DBType.Oracle)
        return;
      this._paramPrefix = ":";
    }

    public void Dispose()
    {
      this.CloseSharedConnection();
    }

    public void OpenSharedConnection()
    {
      if (this._sharedConnectionDepth == 0)
      {
        this._sharedConnection = (IDbConnection) this._factory.CreateConnection();
        this._sharedConnection.ConnectionString = this._connectionString;
        this._sharedConnection.Open();
        this._sharedConnection = this.OnConnectionOpened(this._sharedConnection);
        if (this.KeepConnectionAlive)
          ++this._sharedConnectionDepth;
      }
      ++this._sharedConnectionDepth;
    }

    public void CloseSharedConnection()
    {
      if (this._sharedConnectionDepth <= 0)
        return;
      --this._sharedConnectionDepth;
      if (this._sharedConnectionDepth == 0)
      {
        this.OnConnectionClosing(this._sharedConnection);
        this._sharedConnection.Dispose();
        this._sharedConnection = (IDbConnection) null;
      }
    }

    public Transaction GetTransaction()
    {
      return new Transaction(this);
    }

    public virtual void OnBeginTransaction()
    {
    }

    public virtual void OnEndTransaction()
    {
    }

    public void BeginTransaction()
    {
      ++this._transactionDepth;
      if (this._transactionDepth != 1)
        return;
      this.OpenSharedConnection();
      this._transaction = this._sharedConnection.BeginTransaction();
      this._transactionCancelled = false;
      this.OnBeginTransaction();
    }

    private void CleanupTransaction()
    {
      this.OnEndTransaction();
      if (this._transactionCancelled)
        this._transaction.Rollback();
      else
        this._transaction.Commit();
      this._transaction.Dispose();
      this._transaction = (IDbTransaction) null;
      this.CloseSharedConnection();
    }

    public void AbortTransaction()
    {
      this._transactionCancelled = true;
      if (--this._transactionDepth != 0)
        return;
      this.CleanupTransaction();
    }

    public void CompleteTransaction()
    {
      if (--this._transactionDepth != 0)
        return;
      this.CleanupTransaction();
    }

    public static string ProcessParams(string _sql, object[] args_src, List<object> args_dest)
    {
      return Database.rxParams.Replace(_sql, (MatchEvaluator) (m =>
      {
        string str = m.Value.Substring(1);
        int result;
        object obj1;
        if (int.TryParse(str, out result))
        {
          if (result < 0 || result >= args_src.Length)
            throw new ArgumentOutOfRangeException(string.Format("Parameter '@{0}' specified but only {1} parameters supplied (in `{2}`)", (object) result, (object) args_src.Length, (object) _sql));
          obj1 = args_src[result];
        }
        else
        {
          bool flag = false;
          obj1 = (object) null;
          foreach (object obj2 in args_src)
          {
            PropertyInfo property = obj2.GetType().GetProperty(str);
            if (property != null)
            {
              obj1 = property.GetValue(obj2, (object[]) null);
              flag = true;
              break;
            }
          }
          if (!flag)
            throw new ArgumentException(string.Format("Parameter '@{0}' specified but none of the passed arguments have a property with this name (in '{1}')", (object) str, (object) _sql));
        }
        if (obj1 is IEnumerable && !(obj1 is string) && !(obj1 is byte[]))
        {
          StringBuilder stringBuilder = new StringBuilder();
          foreach (object obj2 in obj1 as IEnumerable)
          {
            stringBuilder.Append((stringBuilder.Length == 0 ? "@" : ",@") + args_dest.Count.ToString());
            args_dest.Add(obj2);
          }
          return stringBuilder.ToString();
        }
        args_dest.Add(obj1);
        return "@" + (args_dest.Count - 1).ToString();
      }));
    }

    private void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
    {
      if (Database.Mapper != null && item != null)
      {
        Func<object, object> toDbConverter = Database.Mapper.GetToDbConverter(item.GetType());
        if (toDbConverter != null)
          item = toDbConverter(item);
      }
      IDbDataParameter dbDataParameter = item as IDbDataParameter;
      if (dbDataParameter != null)
      {
        dbDataParameter.ParameterName = string.Format("{0}{1}", (object) ParameterPrefix, (object) cmd.Parameters.Count);
        cmd.Parameters.Add((object) dbDataParameter);
      }
      else
      {
        IDbDataParameter parameter = cmd.CreateParameter();
        parameter.ParameterName = string.Format("{0}{1}", (object) ParameterPrefix, (object) cmd.Parameters.Count);
        if (item == null)
        {
          parameter.Value = (object) DBNull.Value;
        }
        else
        {
          Type type = item.GetType();
          if (type.IsEnum)
            parameter.Value = (object) (int) item;
          else if (type == typeof (Guid))
          {
            parameter.Value = (object) item.ToString();
            parameter.DbType = DbType.String;
            parameter.Size = 40;
          }
          else if (type == typeof (string))
          {
            parameter.Size = Math.Max((item as string).Length + 1, 4000);
            parameter.Value = item;
          }
          else if (type == typeof (AnsiString))
          {
            parameter.Size = Math.Max((item as AnsiString).Value.Length + 1, 4000);
            parameter.Value = (object) (item as AnsiString).Value;
            parameter.DbType = DbType.AnsiString;
          }
          else if (type == typeof (bool) && this._dbType != Database.DBType.PostgreSQL)
            parameter.Value = (object) ((bool) item ? 1 : 0);
          else if (item.GetType().Name == "SqlGeography")
          {
            parameter.GetType().GetProperty("UdtTypeName").SetValue((object) parameter, (object) "geography", (object[]) null);
            parameter.Value = item;
          }
          else if (item.GetType().Name == "SqlGeometry")
          {
            parameter.GetType().GetProperty("UdtTypeName").SetValue((object) parameter, (object) "geometry", (object[]) null);
            parameter.Value = item;
          }
          else
            parameter.Value = item;
        }
        cmd.Parameters.Add((object) parameter);
      }
    }

    public IDbCommand CreateCommand(IDbConnection connection, string sql, params object[] args)
    {
      if (this.EnableNamedParams)
      {
        List<object> args_dest = new List<object>();
        sql = Database.ProcessParams(sql, args, args_dest);
        args = args_dest.ToArray();
      }
      if (this._paramPrefix != "@")
        sql = Database.rxParamsPrefix.Replace(sql, (MatchEvaluator) (m => this._paramPrefix + m.Value.Substring(1)));
      sql = sql.Replace("@@", "@");
      IDbCommand command = connection.CreateCommand();
      command.Connection = connection;
      command.CommandText = sql;
      command.Transaction = this._transaction;
      foreach (object obj in args)
        this.AddParam(command, obj, this._paramPrefix);
      if (this._dbType == Database.DBType.Oracle)
        command.GetType().GetProperty("BindByName").SetValue((object) command, (object) true, (object[]) null);
      if (!string.IsNullOrEmpty(sql))
        this.DoPreExecute(command);
      return command;
    }

    public virtual void OnException(Exception x)
    {
      System.Diagnostics.Debug.WriteLine(x.ToString());
      System.Diagnostics.Debug.WriteLine(this.LastCommand);
    }

    public virtual IDbConnection OnConnectionOpened(IDbConnection conn)
    {
      return conn;
    }

    public virtual void OnConnectionClosing(IDbConnection conn)
    {
    }

    public virtual void OnExecutingCommand(IDbCommand cmd)
    {
    }

    public virtual void OnExecutedCommand(IDbCommand cmd)
    {
    }

    public int Execute(string sql, params object[] args)
    {
      try
      {
        this.OpenSharedConnection();
        try
        {
          using (IDbCommand command = this.CreateCommand(this._sharedConnection, sql, args))
          {
            int num = command.ExecuteNonQuery();
            this.OnExecutedCommand(command);
            return num;
          }
        }
        finally
        {
          this.CloseSharedConnection();
        }
      }
      catch (Exception ex)
      {
        this.OnException(ex);
        throw;
      }
    }

    public int Execute(Sql sql)
    {
      return this.Execute(sql.SQL, sql.Arguments);
    }

    public T ExecuteScalar<T>(string sql, params object[] args)
    {
      try
      {
        this.OpenSharedConnection();
        try
        {
          using (IDbCommand command = this.CreateCommand(this._sharedConnection, sql, args))
          {
            object obj = command.ExecuteScalar();
            this.OnExecutedCommand(command);
            return (T) Convert.ChangeType(obj, typeof (T));
          }
        }
        finally
        {
          this.CloseSharedConnection();
        }
      }
      catch (Exception ex)
      {
        this.OnException(ex);
        throw;
      }
    }

    public T ExecuteScalar<T>(Sql sql)
    {
      return this.ExecuteScalar<T>(sql.SQL, sql.Arguments);
    }

    private string AddSelectClause<T>(string sql)
    {
      if (sql.StartsWith(";"))
        return sql.Substring(1);
      if (!this.rxSelect.IsMatch(sql))
      {
        Database.PocoData pocoData = Database.PocoData.ForType(typeof (T));
        string tableName = this.EscapeTableName(pocoData.TableInfo.TableName);
        string str = string.Join(", ", ((IEnumerable<string>) pocoData.QueryColumns).Select<string, string>((Func<string, string>) (c => tableName + "." + this.EscapeSqlIdentifier(c))).ToArray<string>());
        sql = this.rxFrom.IsMatch(sql) ? string.Format("SELECT {0} {1}", (object) str, (object) sql) : string.Format("SELECT {0} FROM {1} {2}", (object) str, (object) tableName, (object) sql);
      }
      return sql;
    }

    public List<T> Fetch<T>(string sql, params object[] args)
    {
      return this.Query<T>(sql, args).ToList<T>();
    }

    public List<T> Fetch<T>(Sql sql)
    {
      return this.Fetch<T>(sql.SQL, sql.Arguments);
    }

    public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
    {
      sqlSelectRemoved = (string) null;
      sqlCount = (string) null;
      sqlOrderBy = (string) null;
      Match match1 = Database.rxColumns.Match(sql);
      if (!match1.Success)
        return false;
      Group group1 = match1.Groups[1];
      sqlSelectRemoved = sql.Substring(group1.Index);
      if (Database.rxDistinct.IsMatch(sqlSelectRemoved))
        sqlCount = sql.Substring(0, group1.Index) + "COUNT(" + match1.Groups[1].ToString().Trim() + ") " + sql.Substring(group1.Index + group1.Length);
      else
        sqlCount = sql.Substring(0, group1.Index) + "COUNT(*) " + sql.Substring(group1.Index + group1.Length);
      Match match2 = Database.rxOrderBy.Match(sqlCount);
      if (!match2.Success)
      {
        sqlOrderBy = (string) null;
      }
      else
      {
        Group group2 = match2.Groups[0];
        sqlOrderBy = group2.ToString();
        sqlCount = sqlCount.Substring(0, group2.Index) + sqlCount.Substring(group2.Index + group2.Length);
      }
      return true;
    }

    public void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
    {
      if (this.EnableAutoSelect)
        sql = this.AddSelectClause<T>(sql);
      string sqlSelectRemoved;
      string sqlOrderBy;
      if (!Database.SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
        throw new Exception("Unable to parse SQL statement for paged query");
      if (this._dbType == Database.DBType.Oracle && sqlSelectRemoved.StartsWith("*"))
        throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id");
      if (this._dbType == Database.DBType.SqlServer || this._dbType == Database.DBType.Oracle)
      {
        string input = Database.rxOrderBy.Replace(sqlSelectRemoved, "");
        if (Database.rxDistinct.IsMatch(input))
          input = "peta_inner.* FROM (SELECT " + input + ") peta_inner";
        sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}", sqlOrderBy == null ? (object) "ORDER BY (SELECT NULL)" : (object) sqlOrderBy, (object) input, (object) args.Length, (object) (args.Length + 1));
        args = ((IEnumerable<object>) args).Concat<object>((IEnumerable<object>) new object[2]
        {
          (object) skip,
          (object) (skip + take)
        }).ToArray<object>();
      }
      else if (this._dbType == Database.DBType.SqlServerCE)
      {
        sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", (object) sql, (object) args.Length, (object) (args.Length + 1));
        args = ((IEnumerable<object>) args).Concat<object>((IEnumerable<object>) new object[2]
        {
          (object) skip,
          (object) take
        }).ToArray<object>();
      }
      else
      {
        sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", (object) sql, (object) args.Length, (object) (args.Length + 1));
        args = ((IEnumerable<object>) args).Concat<object>((IEnumerable<object>) new object[2]
        {
          (object) take,
          (object) skip
        }).ToArray<object>();
      }
    }

    public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
    {
      string sqlCount;
      string sqlPage;
      this.BuildPageQueries<T>((page - 1L) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
      int timeCommandTimeout = this.OneTimeCommandTimeout;
      Page<T> page1 = new Page<T>();
      page1.CurrentPage = page;
      page1.ItemsPerPage = itemsPerPage;
      page1.TotalItems = this.ExecuteScalar<long>(sqlCount, args);
      page1.TotalPages = page1.TotalItems / itemsPerPage;
      if (page1.TotalItems % itemsPerPage != 0L)
        ++page1.TotalPages;
      this.OneTimeCommandTimeout = timeCommandTimeout;
      page1.Items = this.Fetch<T>(sqlPage, args);
      return page1;
    }

    public Page<T> Page<T>(long page, long itemsPerPage, Sql sql)
    {
      return this.Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
    }

    public List<T> Fetch<T>(long page, long itemsPerPage, string sql, params object[] args)
    {
      return this.SkipTake<T>((page - 1L) * itemsPerPage, itemsPerPage, sql, args);
    }

    public List<T> Fetch<T>(long page, long itemsPerPage, Sql sql)
    {
      return this.SkipTake<T>((page - 1L) * itemsPerPage, itemsPerPage, sql.SQL, sql.Arguments);
    }

    public List<T> SkipTake<T>(long skip, long take, string sql, params object[] args)
    {
      string sqlCount;
      string sqlPage;
      this.BuildPageQueries<T>(skip, take, sql, ref args, out sqlCount, out sqlPage);
      return this.Fetch<T>(sqlPage, args);
    }

    public List<T> SkipTake<T>(long skip, long take, Sql sql)
    {
      return this.SkipTake<T>(skip, take, sql.SQL, sql.Arguments);
    }

    public IEnumerable<T> Query<T>(string sql, params object[] args)
    {
      if (this.EnableAutoSelect)
        sql = this.AddSelectClause<T>(sql);
      this.OpenSharedConnection();
      try
      {
        using (IDbCommand command = this.CreateCommand(this._sharedConnection, sql, args))
        {
          Database.PocoData pd = Database.PocoData.ForType(typeof (T));
          IDataReader r;
          try
          {
            r = command.ExecuteReader();
            this.OnExecutedCommand(command);
          }
          catch (Exception ex)
          {
            this.OnException(ex);
            throw;
          }
          Func<IDataReader, T> factory = pd.GetFactory(command.CommandText, this._sharedConnection.ConnectionString, this.ForceDateTimesToUtc, 0, r.FieldCount, r) as Func<IDataReader, T>;
          using (r)
          {
            while (true)
            {
              T poco;
              try
              {
                if (!r.Read())
                  break;
                poco = factory(r);
              }
              catch (Exception ex)
              {
                this.OnException(ex);
                throw;
              }
              yield return poco;
            }
          }
        }
      }
      finally
      {
        this.CloseSharedConnection();
      }
    }

    public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args)
    {
      return this.Query<T1, T2, TRet>(cb, sql, args).ToList<TRet>();
    }

    public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args)
    {
      return this.Query<T1, T2, T3, TRet>(cb, sql, args).ToList<TRet>();
    }

    public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args)
    {
      return this.Query<T1, T2, T3, T4, TRet>(cb, sql, args).ToList<TRet>();
    }

    public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args)
    {
      return this.Query<TRet>(new Type[2]
      {
        typeof (T1),
        typeof (T2)
      }, (object) cb, sql, args);
    }

    public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args)
    {
      return this.Query<TRet>(new Type[3]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3)
      }, (object) cb, sql, args);
    }

    public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args)
    {
      return this.Query<TRet>(new Type[4]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3),
        typeof (T4)
      }, (object) cb, sql, args);
    }

    public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql)
    {
      return this.Query<T1, T2, TRet>(cb, sql.SQL, sql.Arguments).ToList<TRet>();
    }

    public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql)
    {
      return this.Query<T1, T2, T3, TRet>(cb, sql.SQL, sql.Arguments).ToList<TRet>();
    }

    public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql)
    {
      return this.Query<T1, T2, T3, T4, TRet>(cb, sql.SQL, sql.Arguments).ToList<TRet>();
    }

    public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql)
    {
      return this.Query<TRet>(new Type[2]
      {
        typeof (T1),
        typeof (T2)
      }, (object) cb, sql.SQL, sql.Arguments);
    }

    public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql)
    {
      return this.Query<TRet>(new Type[3]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3)
      }, (object) cb, sql.SQL, sql.Arguments);
    }

    public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql)
    {
      return this.Query<TRet>(new Type[4]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3),
        typeof (T4)
      }, (object) cb, sql.SQL, sql.Arguments);
    }

    public List<T1> Fetch<T1, T2>(string sql, params object[] args)
    {
      return this.Query<T1, T2>(sql, args).ToList<T1>();
    }

    public List<T1> Fetch<T1, T2, T3>(string sql, params object[] args)
    {
      return this.Query<T1, T2, T3>(sql, args).ToList<T1>();
    }

    public List<T1> Fetch<T1, T2, T3, T4>(string sql, params object[] args)
    {
      return this.Query<T1, T2, T3, T4>(sql, args).ToList<T1>();
    }

    public IEnumerable<T1> Query<T1, T2>(string sql, params object[] args)
    {
      return this.Query<T1>(new Type[2]
      {
        typeof (T1),
        typeof (T2)
      }, (object) null, sql, args);
    }

    public IEnumerable<T1> Query<T1, T2, T3>(string sql, params object[] args)
    {
      return this.Query<T1>(new Type[3]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3)
      }, (object) null, sql, args);
    }

    public IEnumerable<T1> Query<T1, T2, T3, T4>(string sql, params object[] args)
    {
      return this.Query<T1>(new Type[4]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3),
        typeof (T4)
      }, (object) null, sql, args);
    }

    public List<T1> Fetch<T1, T2>(Sql sql)
    {
      return this.Query<T1, T2>(sql.SQL, sql.Arguments).ToList<T1>();
    }

    public List<T1> Fetch<T1, T2, T3>(Sql sql)
    {
      return this.Query<T1, T2, T3>(sql.SQL, sql.Arguments).ToList<T1>();
    }

    public List<T1> Fetch<T1, T2, T3, T4>(Sql sql)
    {
      return this.Query<T1, T2, T3, T4>(sql.SQL, sql.Arguments).ToList<T1>();
    }

    public IEnumerable<T1> Query<T1, T2>(Sql sql)
    {
      return this.Query<T1>(new Type[2]
      {
        typeof (T1),
        typeof (T2)
      }, (object) null, sql.SQL, sql.Arguments);
    }

    public IEnumerable<T1> Query<T1, T2, T3>(Sql sql)
    {
      return this.Query<T1>(new Type[3]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3)
      }, (object) null, sql.SQL, sql.Arguments);
    }

    public IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql)
    {
      return this.Query<T1>(new Type[4]
      {
        typeof (T1),
        typeof (T2),
        typeof (T3),
        typeof (T4)
      }, (object) null, sql.SQL, sql.Arguments);
    }

    private object GetAutoMapper(Type[] types)
    {
      StringBuilder stringBuilder = new StringBuilder();
      foreach (Type type in types)
      {
        stringBuilder.Append(type.ToString());
        stringBuilder.Append(":");
      }
      string @string = stringBuilder.ToString();
      Database.RWLock.EnterReadLock();
      object obj;
      try
      {
        if (Database.AutoMappers.TryGetValue(@string, out obj))
          return obj;
      }
      finally
      {
        Database.RWLock.ExitReadLock();
      }
      Database.RWLock.EnterWriteLock();
      try
      {
        if (Database.AutoMappers.TryGetValue(@string, out obj))
          return obj;
        DynamicMethod dynamicMethod = new DynamicMethod("petapoco_automapper", types[0], types, true);
        ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
        for (int i = 1; i < types.Length; ++i)
        {
          bool flag = false;
          for (int index = i - 1; index >= 0; --index)
          {
            IEnumerable<PropertyInfo> source = ((IEnumerable<PropertyInfo>) types[index].GetProperties()).Where<PropertyInfo>((Func<PropertyInfo, bool>) (p => p.PropertyType == types[i]));
            if (source.Count<PropertyInfo>() != 0)
            {
              if (source.Count<PropertyInfo>() > 1)
                throw new InvalidOperationException(string.Format("Can't auto join {0} as {1} has more than one property of type {0}", (object) types[i], (object) types[index]));
              ilGenerator.Emit(OpCodes.Ldarg_S, index);
              ilGenerator.Emit(OpCodes.Ldarg_S, i);
              ilGenerator.Emit(OpCodes.Callvirt, source.First<PropertyInfo>().GetSetMethod(true));
              flag = true;
            }
          }
          if (!flag)
            throw new InvalidOperationException(string.Format("Can't auto join {0}", (object) types[i]));
        }
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Ret);
        Delegate @delegate = dynamicMethod.CreateDelegate(Expression.GetFuncType(((IEnumerable<Type>) types).Concat<Type>(((IEnumerable<Type>) types).Take<Type>(1)).ToArray<Type>()));
        Database.AutoMappers.Add(@string, (object) @delegate);
        return (object) @delegate;
      }
      finally
      {
        Database.RWLock.ExitWriteLock();
      }
    }

    private Delegate FindSplitPoint(Type typeThis, Type typeNext, string sql, IDataReader r, ref int pos)
    {
      if (typeNext == null)
        return Database.PocoData.ForType(typeThis).GetFactory(sql, this._sharedConnection.ConnectionString, this.ForceDateTimesToUtc, pos, r.FieldCount - pos, r);
      Database.PocoData pocoData1 = Database.PocoData.ForType(typeThis);
      Database.PocoData pocoData2 = Database.PocoData.ForType(typeNext);
      int firstColumn = pos;
      Dictionary<string, bool> dictionary = new Dictionary<string, bool>();
      while (pos < r.FieldCount)
      {
        string name = r.GetName(pos);
        if (dictionary.ContainsKey(name) || !pocoData1.Columns.ContainsKey(name) && pocoData2.Columns.ContainsKey(name))
          return pocoData1.GetFactory(sql, this._sharedConnection.ConnectionString, this.ForceDateTimesToUtc, firstColumn, pos - firstColumn, r);
        dictionary.Add(name, true);
        ++pos;
      }
      throw new InvalidOperationException(string.Format("Couldn't find split point between {0} and {1}", (object) typeThis, (object) typeNext));
    }

    private Func<IDataReader, object, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
    {
      DynamicMethod dynamicMethod = new DynamicMethod("petapoco_multipoco_factory", typeof (TRet), new Type[3]
      {
        typeof (Database.MultiPocoFactory),
        typeof (IDataReader),
        typeof (object)
      }, typeof (Database.MultiPocoFactory));
      ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
      ilGenerator.Emit(OpCodes.Ldarg_2);
      List<Delegate> delegateList = new List<Delegate>();
      int pos = 0;
      for (int index = 0; index < types.Length; ++index)
      {
        Delegate splitPoint = this.FindSplitPoint(types[index], index + 1 < types.Length ? types[index + 1] : (Type) null, sql, r, ref pos);
        delegateList.Add(splitPoint);
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Ldc_I4, index);
        ilGenerator.Emit(OpCodes.Callvirt, typeof (Database.MultiPocoFactory).GetMethod("GetItem"));
        ilGenerator.Emit(OpCodes.Ldarg_1);
        MethodInfo method = splitPoint.GetType().GetMethod("Invoke");
        ilGenerator.Emit(OpCodes.Callvirt, method);
      }
      ilGenerator.Emit(OpCodes.Callvirt, Expression.GetFuncType(((IEnumerable<Type>) types).Concat<Type>((IEnumerable<Type>) new Type[1]{ typeof (TRet) }).ToArray<Type>()).GetMethod("Invoke"));
      ilGenerator.Emit(OpCodes.Ret);
      return (Func<IDataReader, object, TRet>) dynamicMethod.CreateDelegate(typeof (Func<IDataReader, object, TRet>), (object) new Database.MultiPocoFactory()
      {
        m_Delegates = delegateList
      });
    }

    private Func<IDataReader, object, TRet> GetMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
    {
      StringBuilder stringBuilder = new StringBuilder();
      stringBuilder.Append(typeof (TRet).ToString());
      stringBuilder.Append(":");
      foreach (Type type in types)
      {
        stringBuilder.Append(":");
        stringBuilder.Append(type.ToString());
      }
      stringBuilder.Append(":");
      stringBuilder.Append(this._sharedConnection.ConnectionString);
      stringBuilder.Append(":");
      stringBuilder.Append(this.ForceDateTimesToUtc);
      stringBuilder.Append(":");
      stringBuilder.Append(sql);
      string @string = stringBuilder.ToString();
      Database.RWLock.EnterReadLock();
      object obj;
      try
      {
        if (Database.MultiPocoFactories.TryGetValue(@string, out obj))
          return (Func<IDataReader, object, TRet>) obj;
      }
      finally
      {
        Database.RWLock.ExitReadLock();
      }
      Database.RWLock.EnterWriteLock();
      try
      {
        if (Database.MultiPocoFactories.TryGetValue(@string, out obj))
          return (Func<IDataReader, object, TRet>) obj;
        Func<IDataReader, object, TRet> multiPocoFactory = this.CreateMultiPocoFactory<TRet>(types, sql, r);
        Database.MultiPocoFactories.Add(@string, (object) multiPocoFactory);
        return multiPocoFactory;
      }
      finally
      {
        Database.RWLock.ExitWriteLock();
      }
    }

    public IEnumerable<TRet> Query<TRet>(Type[] types, object cb, string sql, params object[] args)
    {
      this.OpenSharedConnection();
      try
      {
        using (IDbCommand command = this.CreateCommand(this._sharedConnection, sql, args))
        {
          IDataReader r;
          try
          {
            r = command.ExecuteReader();
            this.OnExecutedCommand(command);
          }
          catch (Exception ex)
          {
            this.OnException(ex);
            throw;
          }
          Func<IDataReader, object, TRet> factory = this.GetMultiPocoFactory<TRet>(types, sql, r);
          if (cb == null)
            cb = this.GetAutoMapper(((IEnumerable<Type>) types).ToArray<Type>());
          bool bNeedTerminator = false;
          using (r)
          {
            while (true)
            {
              TRet poco;
              try
              {
                if (r.Read())
                  poco = factory(r, cb);
                else
                  break;
              }
              catch (Exception ex)
              {
                this.OnException(ex);
                throw;
              }
              if ((object) poco != null)
                yield return poco;
              else
                bNeedTerminator = true;
            }
            if (bNeedTerminator)
            {
              TRet poco = (TRet) (cb as Delegate).DynamicInvoke(new object[types.Length]);
              if ((object) poco != null)
                yield return poco;
            }
          }
        }
      }
      finally
      {
        this.CloseSharedConnection();
      }
    }

    public IEnumerable<T> Query<T>(Sql sql)
    {
      return this.Query<T>(sql.SQL, sql.Arguments);
    }

    public bool Exists<T>(object primaryKey)
    {
      return (object) this.FirstOrDefault<T>(string.Format("WHERE {0}=@0", (object) this.EscapeSqlIdentifier(Database.PocoData.ForType(typeof (T)).TableInfo.PrimaryKey)), primaryKey) != null;
    }

    public T Single<T>(object primaryKey)
    {
      return this.Single<T>(string.Format("WHERE {0}=@0", (object) this.EscapeSqlIdentifier(Database.PocoData.ForType(typeof (T)).TableInfo.PrimaryKey)), primaryKey);
    }

    public T SingleOrDefault<T>(object primaryKey)
    {
      return this.SingleOrDefault<T>(string.Format("WHERE {0}=@0", (object) this.EscapeSqlIdentifier(Database.PocoData.ForType(typeof (T)).TableInfo.PrimaryKey)), primaryKey);
    }

    public T Single<T>(string sql, params object[] args)
    {
      return this.Query<T>(sql, args).Single<T>();
    }

    public T SingleOrDefault<T>(string sql, params object[] args)
    {
      return this.Query<T>(sql, args).SingleOrDefault<T>();
    }

    public T First<T>(string sql, params object[] args)
    {
      return this.Query<T>(sql, args).First<T>();
    }

    public T FirstOrDefault<T>(string sql, params object[] args)
    {
      return this.Query<T>(sql, args).FirstOrDefault<T>();
    }

    public T Single<T>(Sql sql)
    {
      return this.Query<T>(sql).Single<T>();
    }

    public T SingleOrDefault<T>(Sql sql)
    {
      return this.Query<T>(sql).SingleOrDefault<T>();
    }

    public T First<T>(Sql sql)
    {
      return this.Query<T>(sql).First<T>();
    }

    public T FirstOrDefault<T>(Sql sql)
    {
      return this.Query<T>(sql).FirstOrDefault<T>();
    }

    public string EscapeTableName(string str)
    {
      return str.IndexOf('.') >= 0 ? str : this.EscapeSqlIdentifier(str);
    }

    public string EscapeSqlIdentifier(string str)
    {
      switch (this._dbType)
      {
        case Database.DBType.MySql:
          return string.Format("`{0}`", (object) str);
        case Database.DBType.PostgreSQL:
          return string.Format("\"{0}\"", (object) str);
        case Database.DBType.Oracle:
          return string.Format("\"{0}\"", (object) str.ToUpperInvariant());
        default:
          return string.Format("[{0}]", (object) str);
      }
    }

    public object Insert(string tableName, string primaryKeyName, object poco)
    {
      return this.Insert(tableName, primaryKeyName, true, poco);
    }

    public object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco)
    {
      try
      {
        this.OpenSharedConnection();
        try
        {
          using (IDbCommand command = this.CreateCommand(this._sharedConnection, ""))
          {
            Database.PocoData pocoData = Database.PocoData.ForObject(poco, primaryKeyName);
            List<string> stringList1 = new List<string>();
            List<string> stringList2 = new List<string>();
            int num = 0;
            foreach (KeyValuePair<string, Database.PocoColumn> column in pocoData.Columns)
            {
              if (!column.Value.ResultColumn)
              {
                if (autoIncrement && primaryKeyName != null && string.Compare(column.Key, primaryKeyName, true) == 0)
                {
                  if (this._dbType == Database.DBType.Oracle && !string.IsNullOrEmpty(pocoData.TableInfo.SequenceName))
                  {
                    stringList1.Add(column.Key);
                    stringList2.Add(string.Format("{0}.nextval", (object) pocoData.TableInfo.SequenceName));
                  }
                }
                else
                {
                  stringList1.Add(this.EscapeSqlIdentifier(column.Key));
                  stringList2.Add(string.Format("{0}{1}", (object) this._paramPrefix, (object) num++));
                  this.AddParam(command, column.Value.GetValue(poco), this._paramPrefix);
                }
              }
            }
            command.CommandText = string.Format("INSERT INTO {0} ({1}) VALUES ({2})", (object) this.EscapeTableName(tableName), (object) string.Join(",", stringList1.ToArray()), (object) string.Join(",", stringList2.ToArray()));
            if (!autoIncrement)
            {
              this.DoPreExecute(command);
              command.ExecuteNonQuery();
              this.OnExecutedCommand(command);
              return (object) true;
            }
            object val;
            switch (this._dbType)
            {
              case Database.DBType.SqlServer:
                command.CommandText += ";\nSELECT SCOPE_IDENTITY() AS NewID;";
                this.DoPreExecute(command);
                val = command.ExecuteScalar();
                this.OnExecutedCommand(command);
                break;
              case Database.DBType.SqlServerCE:
                this.DoPreExecute(command);
                command.ExecuteNonQuery();
                this.OnExecutedCommand(command);
                val = this.ExecuteScalar<object>("SELECT @@@IDENTITY AS NewID;", new object[0]);
                break;
              case Database.DBType.PostgreSQL:
                if (primaryKeyName != null)
                {
                  command.CommandText += string.Format("returning {0} as NewID", (object) this.EscapeSqlIdentifier(primaryKeyName));
                  this.DoPreExecute(command);
                  val = command.ExecuteScalar();
                }
                else
                {
                  val = (object) -1;
                  this.DoPreExecute(command);
                  command.ExecuteNonQuery();
                }
                this.OnExecutedCommand(command);
                break;
              case Database.DBType.Oracle:
                if (primaryKeyName != null)
                {
                  command.CommandText += string.Format(" returning {0} into :newid", (object) this.EscapeSqlIdentifier(primaryKeyName));
                  IDbDataParameter parameter = command.CreateParameter();
                  parameter.ParameterName = ":newid";
                  parameter.Value = (object) DBNull.Value;
                  parameter.Direction = ParameterDirection.ReturnValue;
                  parameter.DbType = DbType.Int64;
                  command.Parameters.Add((object) parameter);
                  this.DoPreExecute(command);
                  command.ExecuteNonQuery();
                  val = parameter.Value;
                }
                else
                {
                  val = (object) -1;
                  this.DoPreExecute(command);
                  command.ExecuteNonQuery();
                }
                this.OnExecutedCommand(command);
                break;
              case Database.DBType.SQLite:
                if (primaryKeyName != null)
                {
                  command.CommandText += ";\nSELECT last_insert_rowid();";
                  this.DoPreExecute(command);
                  val = command.ExecuteScalar();
                }
                else
                {
                  val = (object) -1;
                  this.DoPreExecute(command);
                  command.ExecuteNonQuery();
                }
                this.OnExecutedCommand(command);
                break;
              default:
                command.CommandText += ";\nSELECT @@IDENTITY AS NewID;";
                this.DoPreExecute(command);
                val = command.ExecuteScalar();
                this.OnExecutedCommand(command);
                break;
            }
            Database.PocoColumn pocoColumn;
            if (primaryKeyName != null && pocoData.Columns.TryGetValue(primaryKeyName, out pocoColumn))
              pocoColumn.SetValue(poco, pocoColumn.ChangeType(val));
            return val;
          }
        }
        finally
        {
          this.CloseSharedConnection();
        }
      }
      catch (Exception ex)
      {
        this.OnException(ex);
        throw;
      }
    }

    public object Insert(object poco)
    {
      Database.PocoData pocoData = Database.PocoData.ForType(poco.GetType());
      return this.Insert(pocoData.TableInfo.TableName, pocoData.TableInfo.PrimaryKey, pocoData.TableInfo.AutoIncrement, poco);
    }

    public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
    {
      return this.Update(tableName, primaryKeyName, poco, primaryKeyValue, (IEnumerable<string>) null);
    }

    public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns)
    {
      try
      {
        this.OpenSharedConnection();
        try
        {
          using (IDbCommand command = this.CreateCommand(this._sharedConnection, ""))
          {
            StringBuilder stringBuilder = new StringBuilder();
            int num1 = 0;
            Database.PocoData pocoData = Database.PocoData.ForObject(poco, primaryKeyName);
            if (columns == null)
            {
              foreach (KeyValuePair<string, Database.PocoColumn> column in pocoData.Columns)
              {
                if (string.Compare(column.Key, primaryKeyName, true) == 0)
                {
                  if (primaryKeyValue == null)
                    primaryKeyValue = column.Value.GetValue(poco);
                }
                else if (!column.Value.ResultColumn)
                {
                  if (num1 > 0)
                    stringBuilder.Append(", ");
                  stringBuilder.AppendFormat("{0} = {1}{2}", (object) this.EscapeSqlIdentifier(column.Key), (object) this._paramPrefix, (object) num1++);
                  this.AddParam(command, column.Value.GetValue(poco), this._paramPrefix);
                }
              }
            }
            else
            {
              foreach (string column in columns)
              {
                Database.PocoColumn pocoColumn = pocoData.Columns[column];
                if (num1 > 0)
                  stringBuilder.Append(", ");
                stringBuilder.AppendFormat("{0} = {1}{2}", (object) this.EscapeSqlIdentifier(column), (object) this._paramPrefix, (object) num1++);
                this.AddParam(command, pocoColumn.GetValue(poco), this._paramPrefix);
              }
              if (primaryKeyValue == null)
                primaryKeyValue = pocoData.Columns[primaryKeyName].GetValue(poco);
            }
            IDbCommand dbCommand = command;
            string format = "UPDATE {0} SET {1} WHERE {2} = {3}{4}";
            object[] objArray1 = new object[5]
            {
              (object) this.EscapeTableName(tableName),
              (object) stringBuilder.ToString(),
              (object) this.EscapeSqlIdentifier(primaryKeyName),
              (object) this._paramPrefix,
              null
            };
            object[] objArray2 = objArray1;
            int index = 4;
            int num2 = num1;
            int num3 = 1;
            int num4 = num2 + num3;
            // ISSUE: variable of a boxed type
            __Boxed<int> local = (System.ValueType) num2;
            objArray2[index] = (object) local;
            object[] objArray3 = objArray1;
            string str = string.Format(format, objArray3);
            dbCommand.CommandText = str;
            this.AddParam(command, primaryKeyValue, this._paramPrefix);
            this.DoPreExecute(command);
            int num5 = command.ExecuteNonQuery();
            this.OnExecutedCommand(command);
            return num5;
          }
        }
        finally
        {
          this.CloseSharedConnection();
        }
      }
      catch (Exception ex)
      {
        this.OnException(ex);
        throw;
      }
    }

    public int Update(string tableName, string primaryKeyName, object poco)
    {
      return this.Update(tableName, primaryKeyName, poco, (IEnumerable<string>) null);
    }

    public int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns)
    {
      return this.Update(tableName, primaryKeyName, poco, (object) null, columns);
    }

    public int Update(object poco, IEnumerable<string> columns)
    {
      return this.Update(poco, (object) null, columns);
    }

    public int Update(object poco)
    {
      return this.Update(poco, (object) null, (IEnumerable<string>) null);
    }

    public int Update(object poco, object primaryKeyValue)
    {
      return this.Update(poco, primaryKeyValue, (IEnumerable<string>) null);
    }

    public int Update(object poco, object primaryKeyValue, IEnumerable<string> columns)
    {
      Database.PocoData pocoData = Database.PocoData.ForType(poco.GetType());
      return this.Update(pocoData.TableInfo.TableName, pocoData.TableInfo.PrimaryKey, poco, primaryKeyValue, columns);
    }

    public int Update<T>(string sql, params object[] args)
    {
      return this.Execute(string.Format("UPDATE {0} {1}", (object) this.EscapeTableName(Database.PocoData.ForType(typeof (T)).TableInfo.TableName), (object) sql), args);
    }

    public int Update<T>(Sql sql)
    {
      return this.Execute(new Sql(string.Format("UPDATE {0}", (object) this.EscapeTableName(Database.PocoData.ForType(typeof (T)).TableInfo.TableName)), new object[0]).Append(sql));
    }

    public int Delete(string tableName, string primaryKeyName, object poco)
    {
      return this.Delete(tableName, primaryKeyName, poco, (object) null);
    }

    public int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
    {
      Database.PocoColumn pocoColumn;
      if (primaryKeyValue == null && Database.PocoData.ForObject(poco, primaryKeyName).Columns.TryGetValue(primaryKeyName, out pocoColumn))
        primaryKeyValue = pocoColumn.GetValue(poco);
      return this.Execute(string.Format("DELETE FROM {0} WHERE {1}=@0", (object) this.EscapeTableName(tableName), (object) this.EscapeSqlIdentifier(primaryKeyName)), primaryKeyValue);
    }

    public int Delete(object poco)
    {
      Database.PocoData pocoData = Database.PocoData.ForType(poco.GetType());
      return this.Delete(pocoData.TableInfo.TableName, pocoData.TableInfo.PrimaryKey, poco);
    }

    public int Delete<T>(object pocoOrPrimaryKey)
    {
      if (pocoOrPrimaryKey.GetType() == typeof (T))
        return this.Delete(pocoOrPrimaryKey);
      Database.PocoData pocoData = Database.PocoData.ForType(typeof (T));
      return this.Delete(pocoData.TableInfo.TableName, pocoData.TableInfo.PrimaryKey, (object) null, pocoOrPrimaryKey);
    }

    public int Delete<T>(string sql, params object[] args)
    {
      return this.Execute(string.Format("DELETE FROM {0} {1}", (object) this.EscapeTableName(Database.PocoData.ForType(typeof (T)).TableInfo.TableName), (object) sql), args);
    }

    public int Delete<T>(Sql sql)
    {
      return this.Execute(new Sql(string.Format("DELETE FROM {0}", (object) this.EscapeTableName(Database.PocoData.ForType(typeof (T)).TableInfo.TableName)), new object[0]).Append(sql));
    }

    public bool IsNew(string primaryKeyName, object poco)
    {
      Database.PocoColumn pocoColumn;
      object obj;
      if (Database.PocoData.ForObject(poco, primaryKeyName).Columns.TryGetValue(primaryKeyName, out pocoColumn))
      {
        obj = pocoColumn.GetValue(poco);
      }
      else
      {
        PropertyInfo property = poco.GetType().GetProperty(primaryKeyName);
        if (property == null)
          throw new ArgumentException(string.Format("The object doesn't have a property matching the primary key column name '{0}'", (object) primaryKeyName));
        obj = property.GetValue(poco, (object[]) null);
      }
      if (obj == null)
        return true;
      Type type = obj.GetType();
      if (!type.IsValueType)
        return obj == null;
      if (type == typeof (long))
        return (long) obj == 0L;
      if (type == typeof (ulong))
        return (long) (ulong) obj == 0L;
      if (type == typeof (int))
        return (int) obj == 0;
      if (type == typeof (uint))
        return (int) (uint) obj == 0;
      return obj == Activator.CreateInstance(obj.GetType());
    }

    public bool IsNew(object poco)
    {
      Database.PocoData pocoData = Database.PocoData.ForType(poco.GetType());
      if (!pocoData.TableInfo.AutoIncrement)
        throw new InvalidOperationException("IsNew() and Save() are only supported on tables with auto-increment/identity primary key columns");
      return this.IsNew(pocoData.TableInfo.PrimaryKey, poco);
    }

    public void Save(string tableName, string primaryKeyName, object poco)
    {
      if (this.IsNew(primaryKeyName, poco))
        this.Insert(tableName, primaryKeyName, true, poco);
      else
        this.Update(tableName, primaryKeyName, poco);
    }

    public void Save(object poco)
    {
      Database.PocoData pocoData = Database.PocoData.ForType(poco.GetType());
      this.Save(pocoData.TableInfo.TableName, pocoData.TableInfo.PrimaryKey, poco);
    }

    private void DoPreExecute(IDbCommand cmd)
    {
      if (this.OneTimeCommandTimeout != 0)
      {
        cmd.CommandTimeout = this.OneTimeCommandTimeout;
        this.OneTimeCommandTimeout = 0;
      }
      else if (this.CommandTimeout != 0)
        cmd.CommandTimeout = this.CommandTimeout;
      this.OnExecutingCommand(cmd);
      this._lastSql = cmd.CommandText;
      this._lastArgs = cmd.Parameters.Cast<IDataParameter>().Select<IDataParameter, object>((Func<IDataParameter, object>) (parameter => parameter.Value)).ToArray<object>();
    }

    public string FormatCommand(IDbCommand cmd)
    {
      return this.FormatCommand(cmd.CommandText, cmd.Parameters.Cast<IDataParameter>().Select<IDataParameter, object>((Func<IDataParameter, object>) (parameter => parameter.Value)).ToArray<object>());
    }

    public string FormatCommand(string sql, object[] args)
    {
      StringBuilder stringBuilder = new StringBuilder();
      if (sql == null)
        return "";
      stringBuilder.Append(sql);
      if (args != null && args.Length > 0)
      {
        stringBuilder.Append("\n");
        for (int index = 0; index < args.Length; ++index)
          stringBuilder.AppendFormat("\t -> {0}{1} [{2}] = \"{3}\"\n", (object) this._paramPrefix, (object) index, (object) args[index].GetType().Name, args[index]);
        stringBuilder.Remove(stringBuilder.Length - 1, 1);
      }
      return stringBuilder.ToString();
    }

    private enum DBType
    {
      SqlServer,
      SqlServerCE,
      MySql,
      PostgreSQL,
      Oracle,
      SQLite,
    }

    private class MultiPocoFactory
    {
      public List<Delegate> m_Delegates;

      public Delegate GetItem(int index)
      {
        return this.m_Delegates[index];
      }
    }

    public class PocoColumn
    {
      public string ColumnName;
      public PropertyInfo PropertyInfo;
      public bool ResultColumn;

      public virtual void SetValue(object target, object val)
      {
        this.PropertyInfo.SetValue(target, val, (object[]) null);
      }

      public virtual object GetValue(object target)
      {
        return this.PropertyInfo.GetValue(target, (object[]) null);
      }

      public virtual object ChangeType(object val)
      {
        return Convert.ChangeType(val, this.PropertyInfo.PropertyType);
      }
    }

    public class ExpandoColumn : Database.PocoColumn
    {
      public override void SetValue(object target, object val)
      {
        (target as IDictionary<string, object>)[this.ColumnName] = val;
      }

      public override object GetValue(object target)
      {
        object obj = (object) null;
        (target as IDictionary<string, object>).TryGetValue(this.ColumnName, out obj);
        return obj;
      }

      public override object ChangeType(object val)
      {
        return val;
      }
    }

    public class PocoData
    {
      private static ReaderWriterLockSlim RWLock = new ReaderWriterLockSlim();
      private static Dictionary<Type, Database.PocoData> m_PocoDatas = new Dictionary<Type, Database.PocoData>();
      private static List<Func<object, object>> m_Converters = new List<Func<object, object>>();
      private static MethodInfo fnGetValue = typeof (IDataRecord).GetMethod("GetValue", new Type[1]{ typeof (int) });
      private static MethodInfo fnIsDBNull = typeof (IDataRecord).GetMethod("IsDBNull");
      private static FieldInfo fldConverters = typeof (Database.PocoData).GetField("m_Converters", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField);
      private static MethodInfo fnListGetItem = typeof (List<Func<object, object>>).GetProperty("Item").GetGetMethod();
      private static MethodInfo fnInvoke = typeof (Func<object, object>).GetMethod("Invoke");
      private Dictionary<string, Delegate> PocoFactories = new Dictionary<string, Delegate>();
      public Type type;

      public string[] QueryColumns { get; private set; }

      public TableInfo TableInfo { get; private set; }

      public Dictionary<string, Database.PocoColumn> Columns { get; private set; }

      public PocoData()
      {
      }

      public PocoData(Type t)
      {
        this.type = t;
        this.TableInfo = new TableInfo();
        object[] customAttributes1 = t.GetCustomAttributes(typeof (TableNameAttribute), true);
        this.TableInfo.TableName = customAttributes1.Length == 0 ? t.Name : (customAttributes1[0] as TableNameAttribute).Value;
        object[] customAttributes2 = t.GetCustomAttributes(typeof (PrimaryKeyAttribute), true);
        this.TableInfo.PrimaryKey = customAttributes2.Length == 0 ? "ID" : (customAttributes2[0] as PrimaryKeyAttribute).Value;
        this.TableInfo.SequenceName = customAttributes2.Length == 0 ? (string) null : (customAttributes2[0] as PrimaryKeyAttribute).sequenceName;
        this.TableInfo.AutoIncrement = customAttributes2.Length != 0 && (customAttributes2[0] as PrimaryKeyAttribute).autoIncrement;
        if (Database.Mapper != null)
          Database.Mapper.GetTableInfo(t, this.TableInfo);
        bool flag = t.GetCustomAttributes(typeof (ExplicitColumnsAttribute), true).Length > 0;
        this.Columns = new Dictionary<string, Database.PocoColumn>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
        foreach (PropertyInfo property in t.GetProperties())
        {
          object[] customAttributes3 = property.GetCustomAttributes(typeof (ColumnAttribute), true);
          if (flag)
          {
            if (customAttributes3.Length == 0)
              continue;
          }
          else if (property.GetCustomAttributes(typeof (IgnoreAttribute), true).Length != 0)
            continue;
          Database.PocoColumn pocoColumn = new Database.PocoColumn();
          pocoColumn.PropertyInfo = property;
          if (customAttributes3.Length > 0)
          {
            ColumnAttribute columnAttribute = (ColumnAttribute) customAttributes3[0];
            pocoColumn.ColumnName = columnAttribute.Name;
            if (columnAttribute is ResultColumnAttribute)
              pocoColumn.ResultColumn = true;
          }
          if (pocoColumn.ColumnName == null)
          {
            pocoColumn.ColumnName = property.Name;
            if (Database.Mapper != null && !Database.Mapper.MapPropertyToColumn(property, ref pocoColumn.ColumnName, ref pocoColumn.ResultColumn))
              continue;
          }
          this.Columns.Add(pocoColumn.ColumnName, pocoColumn);
        }
        this.QueryColumns = this.Columns.Where<KeyValuePair<string, Database.PocoColumn>>((Func<KeyValuePair<string, Database.PocoColumn>, bool>) (c => !c.Value.ResultColumn)).Select<KeyValuePair<string, Database.PocoColumn>, string>((Func<KeyValuePair<string, Database.PocoColumn>, string>) (c => c.Key)).ToArray<string>();
      }

      public static Database.PocoData ForObject(object o, string primaryKeyName)
      {
        return Database.PocoData.ForType(o.GetType());
      }

      public static Database.PocoData ForType(Type t)
      {
        Database.PocoData.RWLock.EnterReadLock();
        Database.PocoData pocoData;
        try
        {
          if (Database.PocoData.m_PocoDatas.TryGetValue(t, out pocoData))
            return pocoData;
        }
        finally
        {
          Database.PocoData.RWLock.ExitReadLock();
        }
        Database.PocoData.RWLock.EnterWriteLock();
        try
        {
          if (Database.PocoData.m_PocoDatas.TryGetValue(t, out pocoData))
            return pocoData;
          pocoData = new Database.PocoData(t);
          Database.PocoData.m_PocoDatas.Add(t, pocoData);
        }
        finally
        {
          Database.PocoData.RWLock.ExitWriteLock();
        }
        return pocoData;
      }

      private static bool IsIntegralType(Type t)
      {
        TypeCode typeCode = Type.GetTypeCode(t);
        return typeCode >= TypeCode.SByte && typeCode <= TypeCode.UInt64;
      }

      public Delegate GetFactory(string sql, string connString, bool ForceDateTimesToUtc, int firstColumn, int countColumns, IDataReader r)
      {
        string key = string.Format("{0}:{1}:{2}:{3}:{4}", (object) sql, (object) connString, (object) ForceDateTimesToUtc, (object) firstColumn, (object) countColumns);
        Database.PocoData.RWLock.EnterReadLock();
        Delegate delegate1;
        try
        {
          if (this.PocoFactories.TryGetValue(key, out delegate1))
            return delegate1;
        }
        finally
        {
          Database.PocoData.RWLock.ExitReadLock();
        }
        Database.PocoData.RWLock.EnterWriteLock();
        try
        {
          if (this.PocoFactories.TryGetValue(key, out delegate1))
            return delegate1;
          DynamicMethod dynamicMethod = new DynamicMethod("petapoco_factory_" + this.PocoFactories.Count.ToString(), this.type, new Type[1]
          {
            typeof (IDataReader)
          }, 1 != 0);
          ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
          if (this.type.IsValueType || this.type == typeof (string) || this.type == typeof (byte[]))
          {
            Type fieldType = r.GetFieldType(0);
            Func<object, object> converter = Database.PocoData.GetConverter(ForceDateTimesToUtc, (Database.PocoColumn) null, fieldType, this.type);
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldc_I4_0);
            ilGenerator.Emit(OpCodes.Callvirt, Database.PocoData.fnIsDBNull);
            Label label1 = ilGenerator.DefineLabel();
            ilGenerator.Emit(OpCodes.Brfalse_S, label1);
            ilGenerator.Emit(OpCodes.Ldnull);
            Label label2 = ilGenerator.DefineLabel();
            ilGenerator.Emit(OpCodes.Br_S, label2);
            ilGenerator.MarkLabel(label1);
            Database.PocoData.AddConverterToStack(ilGenerator, converter);
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldc_I4_0);
            ilGenerator.Emit(OpCodes.Callvirt, Database.PocoData.fnGetValue);
            if (converter != null)
              ilGenerator.Emit(OpCodes.Callvirt, Database.PocoData.fnInvoke);
            ilGenerator.MarkLabel(label2);
            ilGenerator.Emit(OpCodes.Unbox_Any, this.type);
          }
          else
          {
            ilGenerator.Emit(OpCodes.Newobj, this.type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, (Binder) null, new Type[0], (ParameterModifier[]) null));
            for (int i = firstColumn; i < firstColumn + countColumns; ++i)
            {
              Database.PocoColumn pc;
              if (this.Columns.TryGetValue(r.GetName(i), out pc))
              {
                Type fieldType = r.GetFieldType(i);
                Type propertyType = pc.PropertyInfo.PropertyType;
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldc_I4, i);
                ilGenerator.Emit(OpCodes.Callvirt, Database.PocoData.fnIsDBNull);
                Label label = ilGenerator.DefineLabel();
                ilGenerator.Emit(OpCodes.Brtrue_S, label);
                ilGenerator.Emit(OpCodes.Dup);
                Func<object, object> converter = Database.PocoData.GetConverter(ForceDateTimesToUtc, pc, fieldType, propertyType);
                bool flag = false;
                if (converter == null)
                {
                  MethodInfo method = typeof (IDataRecord).GetMethod("Get" + fieldType.Name, new Type[1]{ typeof (int) });
                  if (method != null && method.ReturnType == fieldType && (method.ReturnType == propertyType || method.ReturnType == Nullable.GetUnderlyingType(propertyType)))
                  {
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldc_I4, i);
                    ilGenerator.Emit(OpCodes.Callvirt, method);
                    if (Nullable.GetUnderlyingType(propertyType) != null)
                      ilGenerator.Emit(OpCodes.Newobj, propertyType.GetConstructor(new Type[1]
                      {
                        Nullable.GetUnderlyingType(propertyType)
                      }));
                    ilGenerator.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true));
                    flag = true;
                  }
                }
                if (!flag)
                {
                  Database.PocoData.AddConverterToStack(ilGenerator, converter);
                  ilGenerator.Emit(OpCodes.Ldarg_0);
                  ilGenerator.Emit(OpCodes.Ldc_I4, i);
                  ilGenerator.Emit(OpCodes.Callvirt, Database.PocoData.fnGetValue);
                  if (converter != null)
                    ilGenerator.Emit(OpCodes.Callvirt, Database.PocoData.fnInvoke);
                  ilGenerator.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType);
                  ilGenerator.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true));
                }
                ilGenerator.MarkLabel(label);
              }
            }
            MethodInfo meth = Database.PocoData.RecurseInheritedTypes<MethodInfo>(this.type, (Func<Type, MethodInfo>) (x => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, (Binder) null, new Type[0], (ParameterModifier[]) null)));
            if (meth != null)
            {
              ilGenerator.Emit(OpCodes.Dup);
              ilGenerator.Emit(OpCodes.Callvirt, meth);
            }
          }
          ilGenerator.Emit(OpCodes.Ret);
          Delegate delegate2 = dynamicMethod.CreateDelegate(Expression.GetFuncType(typeof (IDataReader), this.type));
          this.PocoFactories.Add(key, delegate2);
          return delegate2;
        }
        finally
        {
          Database.PocoData.RWLock.ExitWriteLock();
        }
      }

      private static void AddConverterToStack(ILGenerator il, Func<object, object> converter)
      {
        if (converter == null)
          return;
        int count = Database.PocoData.m_Converters.Count;
        Database.PocoData.m_Converters.Add(converter);
        il.Emit(OpCodes.Ldsfld, Database.PocoData.fldConverters);
        il.Emit(OpCodes.Ldc_I4, count);
        il.Emit(OpCodes.Callvirt, Database.PocoData.fnListGetItem);
      }

      private static Func<object, object> GetConverter(bool forceDateTimesToUtc, Database.PocoColumn pc, Type srcType, Type dstType)
      {
        Func<object, object> func = (Func<object, object>) null;
        if (Database.Mapper != null)
        {
          if (pc != null)
          {
            func = Database.Mapper.GetFromDbConverter(pc.PropertyInfo, srcType);
          }
          else
          {
            IMapper2 mapper2 = Database.Mapper as IMapper2;
            if (mapper2 != null)
              func = mapper2.GetFromDbConverter(dstType, srcType);
          }
        }
        if (forceDateTimesToUtc && func == null && srcType == typeof (DateTime) && (dstType == typeof (DateTime) || dstType == typeof (DateTime?)))
          func = (Func<object, object>) (src => (object) new DateTime(((DateTime) src).Ticks, DateTimeKind.Utc));
        if (func == null)
        {
          if (dstType.IsEnum && Database.PocoData.IsIntegralType(srcType))
          {
            if (srcType != typeof (int))
              func = (Func<object, object>) (src => Convert.ChangeType(src, typeof (int), (IFormatProvider) null));
          }
          else if (!dstType.IsAssignableFrom(srcType))
            func = (Func<object, object>) (src => Convert.ChangeType(src, dstType, (IFormatProvider) null));
        }
        return func;
      }

      private static T RecurseInheritedTypes<T>(Type t, Func<Type, T> cb)
      {
        for (; t != null; t = t.BaseType)
        {
          T obj = cb(t);
          if ((object) obj != null)
            return obj;
        }
        return default (T);
      }
    }
  }
}
